/*
 * Eugen Stoian <stoian.e@gmail.com> 2010
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "UnaryOpNode.h"
#include "ValueNode.h"
/**
 *\fn UnaryOpNode(const QString& info)
 *\brief Constructor
 */
UnaryOpNode::UnaryOpNode(const QString& info):ExpressionTreeNode(),_child(0),_op(0),_derivativeBuilder(0),_info(info){
    _op = FunTable::fun(_info+"_1");
    _derivativeBuilder = FunTable::derivativeBuilder(_info);
}


/**
 *\fn Real UnaryOpNode::eval() const
 *\brief Recursively evaluates contained math expression
 * Reimplemented from ExpressionTreeNode
 *
 */
Real UnaryOpNode::eval() const {
    if(_op && _child){
        return (*_op)(_child->eval());
    }
    return Real::nan();
}

/**
 *\fn ExpressionTreeNode* UnaryOpNode::copy() const
 *\brief Returns the root of simplified version of the tree
 * Reimplemented from ExpressionTreeNode
 */
ExpressionTreeNode* UnaryOpNode::copy() const {
    SmartPtr<ExpressionTreeNode> child = 0;
    ExpressionTreeNode *root = 0;
    if(_child)
        child = _child->copy();
    root = new UnaryOpNode(_info);
    root->setRight(child);
    return root;
}

/**
 *\fn SmartPtr<ExpressionTreeNode> UnaryOpNode::firstLevelSimplification() const
 *\brief
 * Reimplemented from ExpressionTreeNode
 */
SmartPtr<ExpressionTreeNode> UnaryOpNode::firstLevelSimplification() const {
    SmartPtr<ExpressionTreeNode> root = 0;
    if(_op && _child){
        SmartPtr<ExpressionTreeNode> child = _child->firstLevelSimplification();
        if(child->isValue() && !child->isMathConstant()){
            QString branchPath;
            root = new ValueNode((*_op)(child->eval()));
        }
        else{
            root = new UnaryOpNode(_info);
            root->setRight(child);
        }
    }
    return root;
}

/**
 *\fn SmartPtr<ExpressionTreeNode> UnaryOpNode::derive(const QString& varName) const
 *\brief Returns the tree associated to the symbolic math derivative of the contained math expression
 *\param varName Name of the variable with respect to which the derivative is computed
 * Reimplemented from ExpressionTreeNode .
 */
SmartPtr<ExpressionTreeNode> UnaryOpNode::derive(const QString& varName) const {
    SmartPtr<ExpressionTreeNode> der = 0;
    if(_derivativeBuilder && _child){
        der = (*_derivativeBuilder)(this->copy(),varName);
    }
    return der;
}

/**
 *\fn QString UnaryOpNode::toPrefixString() const
 *\brief
 *
 */
QString UnaryOpNode::toPrefixString() const {
    if(_child)
        return _info + " " + _child->toPrefixString();
    return "";
}

/**
 *\fn QString UnaryOpNode::toPostfixString() const
 *\brief
 *
 */
QString UnaryOpNode::toPostfixString() const {
    if(_child)
        return _child->toPostfixString() + " " + _info;
    return "";
}

/**
 *\fn QString UnaryOpNode::toInfixString() const
 *\brief
 *
 */
QString UnaryOpNode::toInfixString() const {
    if(_child) {
        if( (_info!="-") && (_info!="+") )
            return _info + "["+_child->toInfixString()+"]";
        else if(_info=="-"){
            if(_child->isOperand())
                return _info +_child->toInfixString();
            else
                return _info +"("+_child->toInfixString()+")";
        }
        else {
            return _child->toInfixString();
        }
    }
    return "";
}

/**
 *\fn int UnaryOpNode::precedence() const
 *\brief
 *
 */
int UnaryOpNode::precedence() const {
    /*
    if((_info=="-")||(_info=="+"))
        return 7;
    */
    return FUNCALL_PRECEDENCE;
}

/**
 *\fn void UnaryOpNode::bind(int varPos,  const Real * var) const
 *\brief
 *\param varName
 *\param var
 *
 */
void UnaryOpNode::bind(int varPos,  const Real * var) const{
    if(_child){
        _child->bind(varPos,var);
    }
}

/**
 *\fn void UnaryOpNode::bind(const QString& varName,  const Real * var) const
 *\brief
 *\param varName
 *\param var
 *
 */
void UnaryOpNode::bind(const QString& varName,  const Real * var) const{
    if(_child){
        _child->bind(varName,var);
    }
}

/**
 *\fn ExpressionTreeNode* UnaryOpNode::replace(const QString& varName, ExpressionTreeNode * tree) const
 *\brief
 *\param varName
 *\param tree
 *
 */
ExpressionTreeNode* UnaryOpNode::replace(const QString& varName, ExpressionTreeNode* tree) const {
    if(!tree)
        return 0;
    SmartPtr<ExpressionTreeNode> right;
    ExpressionTreeNode* root = 0;
    if(_child){
        right = _child->replace(varName, tree);
        root = new UnaryOpNode(info());
        root->setRight(right);
    }
    return root;
}

/**
 *\fn ExpressionTreeNode* UnaryOpNode::replace(int varPos, ExpressionTreeNode* tree) const
 *\brief
 *\param varPos
 *\param tree
 *
 */
ExpressionTreeNode* UnaryOpNode::replace(int varPos, ExpressionTreeNode* tree) const {
    if(!tree)
        return 0;
    SmartPtr<ExpressionTreeNode> right;
    ExpressionTreeNode* root = 0;
    if(_child){
        right = _child->replace(varPos, tree);
        root = new UnaryOpNode(info());
        root->setRight(right);
    }
    return root;
}

